package han.cirno.corrupt.components.school

import han.cirno.corrupt.components.school.class_time.ClassTime
import han.cirno.corrupt.components.school.term_time.TermTime
import han.cirno.corrupt.data.UserUnstructuredData
import han.cirno.corrupt.data.instances.UserSchoolData
import java.io.Serializable
import kotlin.reflect.KClass

/**
 * Aha, guess what...
 * （天哪我居然会写注释）
 * @author CCirno
 */
interface SchoolProvider{
    companion object{
        const val KEY_USERNAME="key:username"
        const val KEY_TOKEN="key:token"
        const val KEY_PASSWORD="key:password"
        const val KEY_ID="key:student-id"
        const val STRING_INFO="str:info"
        const val INFO_CLASSES="info:classes-table"
        const val INFO_EXAMS="info:exams"
        const val INFO_SCORE="info:score"
        val defaultEditor= DefaultEditor()
    }

    /**
     * Return the name of the school
     * @return name of the school
     */
    fun getSchoolName():String
    /**
     * Return the string info according to the key
     * @return the info string, null if not found
     */
    fun getString(key:String):String?
    /**
     * Get the class time of this school
     * @return the class time of the school
     */
    fun getClassTime(): ClassTime
    /**
     * Get the term time of the school
     * @return the term time of the school
     */
    fun getTermTime(): TermTime
    /**
     * Login to the system
     * @return the result of login
     */
    suspend fun performLogin(userName:String,password:String,callback: LoginCallback):Result<String>
    /**
     * Check weather the school has this info source
     * @param infoKey a key tells implementer which info should it check
     * @return true if the school has the info, false if not
     */
    fun hasInfo(infoKey: String):Boolean
    /**
     * Fetch info from remote server
     * @param infoKey a key tells implementer which info should it get
     * @return the result of fetching
     */
    suspend fun fetchInfo(infoKey:String,vararg params:Any?):Result<Any>
    /**
     * Open a data operation
     * @return the editor object
     */
    fun dataOperation(): Editor {
        return defaultEditor
    }

    /**
     * @author CCirno
     */
    interface LoginCallback{
        fun onProgress(infoStatement: String)
        fun onPartialFailed(
            infoStatement: String,
            fatal: Boolean = false,
            exception: Exception
        )
    }

    interface Editor{
        fun <T:Serializable> singleGet(n:String,kClass: KClass<T>):T?
        fun <T:Serializable> get(n:String,kClass: KClass<T>,l:(T)->Unit): Editor
        fun <T:Serializable> write(n: String, v: T?): Editor
        fun <T:Serializable> gaw(m:String,kClass: KClass<T>,l:(T)->T): Editor
        fun writeNull(m:String): Editor
        fun save(): Editor
    }

    class DefaultEditor: Editor {
        override fun <T : Serializable> singleGet(n: String, kClass: KClass<T>): T? {
            var t:T?=null
            get(n,kClass){t=it}
            return t
        }
        override fun <T : Serializable> get(n: String, kClass: KClass<T>, l: (T) -> Unit): Editor {
            l(UserSchoolData.instance.get(n,kClass)?:return this)
            return this
        }
        override fun <T : Serializable> write(n: String, v: T?): Editor {
            UserSchoolData.instance.put(n,v)
            return this
        }
        override fun <T : Serializable> gaw(m: String, kClass: KClass<T>, l: (T) -> T): Editor {
            UserSchoolData.instance.put(m,l(UserSchoolData.instance.get(m,kClass)?:return this))
            return this
        }
        override fun writeNull(m: String): Editor {
            write(m, null)
            return this
        }
        override fun save(): Editor {
            UserUnstructuredData.saveData()
            return this
        }
    }
}